﻿#region File Description
//-----------------------------------------------------------------------------
// Level.cs
//
// Attack of the Crazy Pixels
// Copyright (c) 2009 VosSoft
//-----------------------------------------------------------------------------
#endregion
#region License
//-----------------------------------------------------------------------------
// The MIT License (MIT)
//
// Copyright (c) 2009 VosSoft
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace Attack_of_the_Crazy_Pixels
{
    /// <summary>
    /// Defines a Level.
    /// </summary>
    public class Level
    {
        #region Fields

        // debug variables
        public static bool DrawBackgroundTexture = true;
        public static bool? DrawPath = true; // tristate
        public static bool DrawTowers = true;
        public static bool DrawTowerCircles = false;
        public static bool DrawTowerTargetLine = false;
        public static bool DrawProjectiles = true;
        public static bool DrawEnemies = true;
        public static bool DrawText = false;

        const float waveTime = 60.0f;

        Player player;
        Texture2D backgroundTexture;
        Color cursorColor, towerColor, textColor, errorColor;
        SoundBank soundBank;

        Polygone path;
        List<Wave> waves = new List<Wave>();
        List<Enemy> enemies = new List<Enemy>();
        List<Tower> towers = new List<Tower>();
        Tower selectedTower = null;

        int wave = 0;
        float timeSinceLastWave = 0.0f;

        float enemyHitpoints = 1.0f;
        int enemiesKilled = 0;
        int enemiesDestinationReached = 0;

        #endregion

        #region Properties

        public Player Player
        {
            get { return player; }
        }

        public Color CursorColor
        {
            get { return cursorColor; }
        }

        public Color TowerColor
        {
            get { return towerColor; }
        }

        public Color TextColor
        {
            get { return textColor; }
        }

        public Color ErrorColor
        {
            get { return errorColor; }
        }

        /// <summary>
        /// Path where the enemies walk along.
        /// </summary>
        public Polygone Path
        {
            get { return path; }
        }

        public List<Wave> Waves
        {
            get { return waves; }
        }

        public List<Enemy> Enemies
        {
            get { return enemies; }
        }

        public List<Tower> Towers
        {
            get { return towers; }
        }

        public Tower SelectedTower
        {
            get { return selectedTower; }
            set { selectedTower = value; }
        }

        public int Wave
        {
            get { return wave; }
        }

        public float TimeUntilNextWave
        {
            get { return (waveTime - timeSinceLastWave); }
        }

        public int EnemiesKilled
        {
            get { return enemiesKilled; }
            set { enemiesKilled = value; }
        }

        public int EnemiesDestinationReached
        {
            get { return enemiesDestinationReached; }
            set { enemiesDestinationReached = value; }
        }

        #endregion

        #region Initialization

        /// <summary>
        /// Initializes a new Level.
        /// </summary>
        /// <param name="player">The current Player.</param>
        /// <param name="backgroundTexture">The background Texture.</param>
        /// <param name="cursorColor">The Color of the Cursor.</param>
        /// <param name="towerColor">The Color of the Tower range circle.</param>
        /// <param name="textColor">The Color of text.</param>
        /// <param name="errorColor">The Color of any error indicators.</param>
        /// <param name="soundBank">SoundBank for playing sounds.</param>
        /// <param name="pathPoints">Path where the Enemies walks along.</param>
        public Level(Player player, Texture2D backgroundTexture, Color cursorColor, Color towerColor, Color textColor,
            Color errorColor, SoundBank soundBank, params Vector2[] pathPoints)
        {
            this.player = player;
            this.backgroundTexture = backgroundTexture;
            this.cursorColor = cursorColor;
            this.towerColor = towerColor;
            this.textColor = textColor;
            this.errorColor = errorColor;
            this.soundBank = soundBank;
            path = new Polygone(2.0f, cursorColor, false, pathPoints);
        }

        #endregion

        #region Update and Draw

        /// <summary>
        /// Updates all Enemies and Towers.
        /// </summary>
        /// <param name="elapsedTime">Time since the last frame (in seconds).</param>
        public void Update(float elapsedTime)
        {
            timeSinceLastWave += elapsedTime;
            if (timeSinceLastWave >= waveTime)
            {
                wave++;
                waves.Add(new Wave(enemyHitpoints, wave, this));
                // the hitpoints start very low:
                // 1, 5, 10, 20, 30, 40, 50, ...
                if (enemyHitpoints >= 10.0f)
                {
                    enemyHitpoints += 10.0f;
                }
                else if (enemyHitpoints == 5.0f)
                {
                    enemyHitpoints = 10.0f;
                }
                else
                {
                    enemyHitpoints = 5.0f;
                }
                timeSinceLastWave -= waveTime;
            }

            // need to be a for-loop, because the Wave can remove itself from the list.
            for (int i = 0; i < waves.Count; i++)
            {
                waves[i].Update(elapsedTime);
            }

            // need to be a for-loop, because the Enemy can remove itself from the list.
            for (int i = 0; i < enemies.Count; i++)
            {
                enemies[i].Move(elapsedTime);
            }

            // first update all WeaponTowers...
            foreach (WeaponTower tower in towers.OfType<WeaponTower>())
            {
                tower.Update(elapsedTime);
            }

            // ... then apply all support effects from the SupportTowers.
            foreach (SupportTower tower in towers.OfType<SupportTower>())
            {
                tower.Update(elapsedTime);
            }
        }

        /// <summary>
        /// Draws the level on the GameplayScreen.
        /// </summary>
        /// <param name="elapsedTime">Time since the last frame (in seconds).</param>
        /// <param name="font">SpriteFont for drawing all the Text in this Level.</param>
        public void Draw(float elapsedTime, SpriteFont font)
        {
            SpriteManager.Begin();

            if (backgroundTexture != null && DrawBackgroundTexture)
            {
                SpriteManager.SpriteBatch.Draw(backgroundTexture, new Rectangle(0, 0, 1280, 720), Color.White);
            }

            if (DrawPath != null)
            {
                foreach (Line line in path.Lines)
                {
                    SpriteManager.Draw(line.LineSprite);
                }
                if (DrawPath == true)
                {
                    foreach (Vector2 point in path.Points)
                    {
                        SpriteManager.Draw(new Sprite(SpriteManager.BlankTexture, point, null, cursorColor, 0.0f,
                            new Vector2(0.5f), new Vector2(10.0f), SpriteEffects.None, 0.0f));
                    }
                }
            }

            if (DrawTowers)
            {
                foreach (Tower tower in towers)
                {
                    if (tower is WeaponTower)
                    {
                        WeaponTower weaponTower = (WeaponTower)tower;

                        if (weaponTower.Target != null && DrawTowerTargetLine)
                        {
                            SpriteManager.Draw(new Line(weaponTower.Position, weaponTower.Target.Position, 1.0f, errorColor).LineSprite);
                        }

                        if (DrawTowerCircles || tower == selectedTower)
                        {
                            Circle towerCircle = new Circle(weaponTower.Position, weaponTower.Range, 32,
                                weaponTower.Target == null, 0.0f, 2.0f, towerColor);
                            foreach (Line line in towerCircle.Lines)
                            {
                                SpriteManager.Draw(line.LineSprite);
                            }
                        }

                        if (DrawProjectiles)
                        {
                            foreach (Projectile projectile in weaponTower.Projectiles)
                            {
                                SpriteManager.Draw(projectile);
                            }
                        }

                        SpriteManager.Draw(weaponTower);

                        if (DrawText || tower == selectedTower)
                        {
                            string towerText = Math.Round(weaponTower.Damage, 1).ToString() + "\n"
                                + Math.Round(weaponTower.FireRate, 1).ToString();
                            SpriteManager.SpriteBatch.DrawString(font, towerText,
                                tower.Position - font.MeasureString(towerText) / 2.0f, Color.White);
                        }
                    }
                    else // SupportTower
                    {
                        SupportTower supportTower = (SupportTower)tower;

                        if (DrawTowerCircles || tower == selectedTower)
                        {
                            Circle towerCircle = new Circle(supportTower.Position, supportTower.Range, 32,
                                true, 0.0f, 2.0f, towerColor);
                            foreach (Line line in towerCircle.Lines)
                            {
                                SpriteManager.Draw(line.LineSprite);
                            }
                        }

                        SpriteManager.Draw(supportTower);

                        if (DrawText || tower == selectedTower)
                        {
                            string towerText = Math.Round(supportTower.RangeMultiplier, 1).ToString() + "\n"
                                + supportTower.WeaponTowers.Count.ToString();
                            SpriteManager.SpriteBatch.DrawString(font, towerText,
                                tower.Position - font.MeasureString(towerText) / 2.0f, Color.White);
                        }
                    }
                }
            }

            if (DrawEnemies)
            {
                foreach (Enemy enemy in enemies)
                {
                    SpriteManager.Draw(new Sprite(SpriteManager.BlankTexture, enemy.Position - new Vector2(0.0f, 14.0f), null,
                        Color.Green, 0.0f, new Vector2(0.5f), new Vector2(enemy.HitPointsPercentage * 16.0f, 3.0f),
                        SpriteEffects.None, 0.0f));

                    SpriteManager.Draw(enemy);

                    if (DrawText)
                    {
                        string enemyText = Math.Round(enemy.HitPoints, 0).ToString();
                        SpriteManager.SpriteBatch.DrawString(font, enemyText,
                            enemy.Position - font.MeasureString(enemyText) / 2.0f - new Vector2(0.0f, 20.0f), textColor);
                    }
                }
            }

            SpriteManager.End();
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds a new Tower to the Level.
        /// </summary>
        /// <param name="towerInfo">The type of the new Tower.</param>
        /// <param name="position">Position of the new Tower.</param>
        public void addTower(TowerInfo towerInfo, Vector2 position)
        {
            if (towerInfo is WeaponTowerInfo)
            {
                selectedTower = new WeaponTower((WeaponTowerInfo)towerInfo, position, this);

                // because we can't know which SupportTower has the new tower in range,
                // we have to check that first and then rescan for all WeaponTowers.
                foreach (SupportTower tower in towers.OfType<SupportTower>())
                {
                    if ((selectedTower.Position - tower.Position).Length() <= tower.Range)
                    {
                        tower.AddWeaponTower((WeaponTower)selectedTower);
                    }
                }
            }
            else // towerInfo is SupportTowerInfo
            {
                selectedTower = new SupportTower((SupportTowerInfo)towerInfo, position, this);
            }

            //playSound("Big_Metal_Impact");
            towers.Add(selectedTower);
        }

        /// <summary>
        /// Removes the SelectedTower from the Level.
        /// </summary>
        public void removeTower()
        {
            if (selectedTower is WeaponTower)
            {
                // instead of searching for all suitable SupportTowers,
                // just try to remove this tower from all SupoertTowers. ;)
                foreach (SupportTower tower in towers.OfType<SupportTower>())
                {
                    tower.WeaponTowers.Remove((WeaponTower)selectedTower);
                }
            }

            player.Money += selectedTower.Info.Costs[0] * (selectedTower.UpgradeLevel + 1) / 2;
            towers.Remove(selectedTower);
            selectedTower = null;
        }

        /// <summary>
        /// Immediately send the next Wave.
        /// </summary>
        public void NextWave()
        {
            timeSinceLastWave = waveTime;
        }

        /// <summary>
        /// Plays a sound from the SoundBank.
        /// </summary>
        /// <param name="name">Name of the sound to play.</param>
        public void playSound(string name)
        {
            soundBank.PlayCue(name);
        }

        #endregion
    }
}
